--- /dev/null
+/* Error states
+ *
+ * GtkLabel and GtkEntry can indicate errors if you set the .error
+ * style class on them.
+ *
+ * This examples shows how this can be used in a dialog for input validation.
+ *
+ * It also shows how pass callbacks and objects to GtkBuilder with
+ * GtkBuilderScope and gtk_builder_expose_object().
+ */
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+static void
+validate_more_details (GtkEntry *entry,
+ GParamSpec *pspec,
+ GtkEntry *details)
+{
+ if (strlen (gtk_editable_get_text (GTK_EDITABLE (entry))) > 0 &&
+ strlen (gtk_editable_get_text (GTK_EDITABLE (details))) == 0)
+ {
+ gtk_widget_set_tooltip_text (GTK_WIDGET (entry), "Must have details first");
+ gtk_widget_add_css_class (GTK_WIDGET (entry), "error");
+ }
+ else
+ {
+ gtk_widget_set_tooltip_text (GTK_WIDGET (entry), "");
+ gtk_widget_remove_css_class (GTK_WIDGET (entry), "error");
+ }
+}
+
+static gboolean
+mode_switch_state_set (GtkSwitch *sw,
+ gboolean state,
+ GtkWidget *scale)
+{
+ GtkWidget *label;
+
+ label = GTK_WIDGET (g_object_get_data (G_OBJECT (sw), "error_label"));
+
+ if (!state ||
+ (gtk_range_get_value (GTK_RANGE (scale)) > 50))
+ {
+ gtk_widget_hide (label);
+ gtk_switch_set_state (sw, state);
+ }
+ else
+ {
+ gtk_widget_show (label);
+ }
+
+ return TRUE;
+}
+
+static void
+level_scale_value_changed (GtkRange *range,
+ GtkWidget *sw)
+{
+ GtkWidget *label;
+
+ label = GTK_WIDGET (g_object_get_data (G_OBJECT (sw), "error_label"));
+
+ if (gtk_switch_get_active (GTK_SWITCH (sw)) &&
+ !gtk_switch_get_state (GTK_SWITCH (sw)) &&
+ (gtk_range_get_value (range) > 50))
+ {
+ gtk_widget_hide (label);
+ gtk_switch_set_state (GTK_SWITCH (sw), TRUE);
+ }
+ else if (gtk_switch_get_state (GTK_SWITCH (sw)) &&
+ (gtk_range_get_value (range) <= 50))
+ {
+ gtk_switch_set_state (GTK_SWITCH (sw), FALSE);
+ }
+}
+
+GtkWidget *
+do_errorstates (GtkWidget *do_widget)
+{
+ static GtkWidget *window = NULL;
+
+ if (!window)
+ {
+ GtkWidget *toplevel;
+ GtkBuilder *builder;
+ GtkBuilderScope *scope;
+ GtkWidget *sw, *label;
+
+ toplevel = GTK_WIDGET (gtk_widget_get_root (do_widget));
+
+ scope = gtk_builder_cscope_new ();
+ gtk_builder_cscope_add_callback_symbols (GTK_BUILDER_CSCOPE (scope),
+ "validate_more_details", G_CALLBACK (validate_more_details),
+ "mode_switch_state_set", G_CALLBACK (mode_switch_state_set),
+ "level_scale_value_changed", G_CALLBACK (level_scale_value_changed),
+ NULL);
+
+ builder = gtk_builder_new ();
+ gtk_builder_set_scope (builder, scope);
+ gtk_builder_expose_object (builder, "toplevel", G_OBJECT (toplevel));
+ gtk_builder_add_from_resource (builder, "/errorstates/errorstates.ui", NULL);
+
+ window = GTK_WIDGET (gtk_builder_get_object (builder, "dialog"));
+
+ gtk_window_set_display (GTK_WINDOW (window),
+ gtk_widget_get_display (do_widget));
+ g_object_add_weak_pointer (G_OBJECT (window), (gpointer *)&window);
+
+ sw = GTK_WIDGET (gtk_builder_get_object (builder, "mode_switch"));
+ label = GTK_WIDGET (gtk_builder_get_object (builder, "error_label"));
+ g_object_set_data (G_OBJECT (sw), "error_label", label);
+
+ g_object_unref (builder);
+ g_object_unref (scope);
+ }
+
+ if (!gtk_widget_get_visible (window))
+ gtk_widget_show (window);
+ else
+ gtk_window_destroy (GTK_WINDOW (window));
+
+ return window;
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <object class="GtkDialog" id="dialog">
+ <property name="transient-for">toplevel</property>
+ <property name="modal">1</property>
+ <property name="resizable">0</property>
+ <property name="use-header-bar">1</property>
+ <property name="title" translatable="yes">Settings</property>
+ <property name="hide-on-close">1</property>
+ <child internal-child="content_area">
+ <object class="GtkBox">
+ <child>
+ <object class="GtkGrid">
+ <property name="row-spacing">10</property>
+ <property name="column-spacing">10</property>
+ <property name="margin-start">20</property>
+ <property name="margin-end">20</property>
+ <property name="margin-top">20</property>
+ <property name="margin-bottom">20</property>
+ <child>
+ <object class="GtkLabel">
+ <property name="halign">end</property>
+ <property name="valign">baseline</property>
+ <property name="label">_Details</property>
+ <property name="use-underline">1</property>
+ <property name="mnemonic-widget">details_entry</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">0</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="details_entry">
+ <property name="valign">baseline</property>
+ <signal name="notify::text" handler="validate_more_details" object="more_details_entry" swapped="yes"/>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">0</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="halign">end</property>
+ <property name="valign">baseline</property>
+ <property name="label">More D_etails</property>
+ <property name="use-underline">1</property>
+ <property name="mnemonic-widget">more_details_entry</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">1</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkEntry" id="more_details_entry">
+ <property name="valign">baseline</property>
+ <signal name="notify::text" handler="validate_more_details" object="details_entry" swapped="no"/>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">1</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="halign">end</property>
+ <property name="valign">baseline</property>
+ <property name="label">_Level</property>
+ <property name="use-underline">1</property>
+ <property name="mnemonic-widget">level_scale</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkScale" id="level_scale">
+ <property name="valign">baseline</property>
+ <property name="draw-value">0</property>
+ <property name="adjustment">
+ <object class="GtkAdjustment">
+ <property name="upper">100</property>
+ <property name="lower">0</property>
+ <property name="value">50</property>
+ <property name="step-increment">1</property>
+ <property name="page-increment">10</property>
+ </object>
+ </property>
+ <signal name="value-changed" handler="level_scale_value_changed" object="mode_switch" swapped="no"/>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">2</property>
+ <property name="column-span">2</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="halign">end</property>
+ <property name="valign">baseline</property>
+ <property name="label">_Mode</property>
+ <property name="use-underline">1</property>
+ <property name="mnemonic-widget">mode_switch</property>
+ <style>
+ <class name="dim-label"/>
+ </style>
+ <layout>
+ <property name="column">0</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkSwitch" id="mode_switch">
+ <property name="halign">start</property>
+ <property name="valign">baseline</property>
+ <signal name="state-set" handler="mode_switch_state_set" object="level_scale" swapped="no"/>
+ <layout>
+ <property name="column">1</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel" id="error_label">
+ <property name="visible">0</property>
+ <property name="halign">start</property>
+ <property name="valign">baseline</property>
+ <property name="label">Level too low</property>
+ <style>
+ <class name="error"/>
+ </style>
+ <layout>
+ <property name="column">2</property>
+ <property name="row">3</property>
+ </layout>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface>